package org.duoduo.common.redis.jedis3_1_0.impl;


import com.duoduo.common.api.constants.LogConstant;
import com.duoduo.common.api.exception.lock.LockRedisException;
import com.duoduo.commonn.redis.LogThreadLocal;
import com.duoduo.commonn.redis.myinterface.RedisOperator;
import org.duoduo.common.redis.jedis3_1_0.JedisCacheInit;
import org.duoduo.common.redis.jedis3_1_0.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.*;

public class JedisOperatorImpl implements RedisOperator {

	private static final Logger logger = LoggerFactory.getLogger(JedisOperatorImpl.class);

	public JedisOperatorImpl(){
		JedisCacheInit.initCluster();
	}

	@Override
	public boolean setNxPx(String redisKey, String appName, String lockKey, String requestId, long expireMsTime) {
		return RedisUtil.setNxPx(redisKey, appName, lockKey, requestId, expireMsTime);
	}

	@Override
	public <T extends Serializable> boolean setNxPxImplSeri(String redisKey, String appName, String key, T value, long expireMsTime) {
		return RedisUtil.setNxPxImplSeri(redisKey, appName, key, value, expireMsTime);
	}

	@Override
	public Object eval(String redisKey, String appName, String script, List<String> keys, List<String> params) {
		return RedisUtil.eval(redisKey, appName, script, keys, params);
	}

	@Override
	public Object evalByte(String redisKey, String appName, String script, List<String> keys, List<byte[]> params) {
		return RedisUtil.evalByte(redisKey, appName, script, keys, params);
	}

	@Override
	public String get(String redisKey, String appName, String key) {
		return RedisUtil.get(redisKey, appName, key);
	}

	@Override
	public List<String> mget(String redisKey, String appName, String[] keys) {
		return RedisUtil.mget(redisKey, appName, keys);
	}
	
	@Override
	public <T extends Serializable> T getObjImplSeri(String redisKey, String appName, String key, Class<T> clazz) {
		return RedisUtil.getObjImplSeri(redisKey, appName, key, clazz);
	}
	
	@Override
	public Boolean exists(String redisKey, String appName, String key) {
		return RedisUtil.exists(redisKey, appName, key);
	}

	@Override
	public Boolean existsSeri(String redisKey, String appName, String key) {
		return RedisUtil.existsSeri(redisKey, appName, key);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Object pop(String redisKey, String appName, String key) {
		String script = "local v = redis.call('get', KEYS[1]) "
				+ "if v == nil "
				+ "then return v "
				+ "else redis.call('del', KEYS[1]); return v "
				+ "end";
		return RedisUtil.evalByte(redisKey, appName, script, Collections.singletonList(key), Collections.EMPTY_LIST);
	}

	@Override
	public Long delKey(String redisKey, String appName, String key) {
		return RedisUtil.delKey(redisKey, appName, key);
	}

	@Override
	public String set(String redisKey, String appName, String key, String value, int expire) {
		return RedisUtil.set(redisKey, appName, key, value, expire);
	}

	@Override
	public <T extends Serializable> String setImplSeri(String redisKey, String appName, String key, T value, int expire) {
		return RedisUtil.setImplSeri(redisKey, appName, key, value, expire);
	}
	
	@Override
	public long incr(String redisKey, String appName, String key) {
		return RedisUtil.incr(redisKey, appName, key);
	}
	
	@Override
	public long decr(String redisKey, String appName, String key) {
		return RedisUtil.decr(redisKey, appName, key);
	}

	@SuppressWarnings({ "unchecked", "rawtypes", "serial" })
	@Override
	public Object setNxAndResetTTL(String redisKey, String appName, String key, String value, int expire) {
		// 由于redis.call('set', KEYS[1], ARGV[1], 'NX', 'EX', tonumber(ARGV[2]))在成功的时候返回一个table类型，失败的时候返回boolean类型，
		// Redis status reply -> Lua table with a single ok field containing the status / Redis 状态回复转换成 Lua 表，表内的 ok 域包含了状态信息
		// Redis error reply -> Lua table with a single err field containing the error / Redis 错误回复转换成 Lua 表，表内的 err 域包含了错误信息
		String script =   "local v = redis.call('set', KEYS[1], ARGV[1], 'NX', 'EX', tonumber(ARGV[2])) " // setnx
						+ "local typestr = type(v) "
						+ "if typestr == 'table' " // 如果返回table，说明可能添加成功
							+ "then return v " // 返回v
						+ "else local lockKey = redis.call('get', KEYS[1]) " // 返回不是table，获取value
						+ "if lockKey == ARGV[1] " // 如果value == 传入的value，说明是重入
							+ "then redis.call('expire', KEYS[1], tonumber(ARGV[2])); " // 延长过期时间
								+ "return 2 " // 返回2
							+ "else return lockKey " // 如果value != 传入的value，返回redis里的value
							+ "end "
						+ "end"; // 如果最后返回null，说明是redis异常
		Object result = RedisUtil.eval(redisKey, appName, script, Collections.singletonList(key), new ArrayList(){{add(value); add(String.valueOf(expire)); }});
		if(result == null) {
			String msg = LogConstant.REDIS_LOCK + " add redis lock unknow error";
			logger.error(msg);
			throw new LockRedisException(msg);
		}
		if(result instanceof Long) {
			if(!LogThreadLocal.restrainLockLog.get()) {
				logger.info(LogConstant.REDIS_LOCK + " add redis lock reenter success, appName: {}, key: {}, requestId: {}, result: {}", appName, key, value, result);
			}
			return result;
		}
		if(result instanceof String) {
			if(result.equals("OK")){
				if(!LogThreadLocal.restrainLockLog.get()) {
					logger.info(LogConstant.REDIS_LOCK + " add redis lock first success, appName: {}, key: {}, requestId: {}", appName, key, value);
				}
				return 1l;
			}else{
				if(!LogThreadLocal.restrainLockLog.get()) {
					logger.warn(LogConstant.REDIS_LOCK + " add redis lock fail, already exist appName: {}, key: {}, current redis requestId: {}, requestId: {}", appName, key, result, value);
				}
				return -1l;
			}
		}
		logger.error(LogConstant.REDIS_LOCK + " add redis lock unknow error, appName: " + appName + ", key: " + key + ", result: " + result);
		return -1l;
	}

	@Override
	public Long addWithSortedSet(String redisKey, String appName, String key, double score, String member) {
		return RedisUtil.addWithSortedSet(redisKey, appName, key, score, member);
	}

	@Override
	public Long zrem(String redisKey, String appName, String key, Collection<String> members) {
		String[] a = new String[members.size()];
		String[] memberArr = members.toArray(a);
		return RedisUtil.zrem(redisKey, appName, key, memberArr);
	}

	@Override
	public Set<String> rangeByScoreWithSortedSet(String redisKey, String appName, String key, double max, double min, int offset, int count) {
		return RedisUtil.rangeByScoreWithSortedSet(redisKey, appName, key, max, min, offset, count);
	}
	
	@Override
	public Long hincrBy(String redisKey, String appName, String key, String field, Long value) {
		return RedisUtil.hincrBy(redisKey, appName, key, field, value);
	}
	
	@Override
	public Long hashSet(String redisKey, String appName, String key, String field, String value) {
		return RedisUtil.hashSet(redisKey, appName, key, field, value);
	}
	
	@Override
	public Long hashSet(String redisKey, String appName, String key, String field, String value, int expire) {
		return RedisUtil.hashSet(redisKey, appName, key, field, value, expire);
	}
	
	@Override
	public <T extends Serializable> Long hashSetImplSeri(String redisKey, String appName, String key, String field, T value) {
		return RedisUtil.hashSetImplSeri(redisKey, appName, key, field, value);
	}
	
	@Override
	public <T extends Serializable> Long hashSetImplSeri(String redisKey, String appName, String key, String field, T value, int expire) {
		return RedisUtil.hashSetImplSeri(redisKey, appName, key, field, value, expire);
	}

	@Override
	public String hashGet(String redisKey, String appName, String key, String field) {
		return RedisUtil.hashGet(redisKey, appName, key, field);
	}

	@Override
	public <T extends Serializable> T hashGetImplSeri(String redisKey, String appName, String key, String field, Class<T> clazz) {
		return RedisUtil.hashGetImplSeri(redisKey, appName, key, field, clazz);
	}

	@Override
	public Map<String, String> hashGetAll(String redisKey, String appName, String key) {
		return RedisUtil.hashGetAll(redisKey, appName, key);
	}

	@Override
	public <T extends Serializable> Map<String, T> hashGetAllImplSeri(String redisKey, String appName, String key, Class<T> clazz) {
		return RedisUtil.hashGetAllImplSeri(redisKey, appName, key, clazz);
	}
	
	@Override
	public Long listPushTail(String redisKey, String appName, String key, String... values) {
		return RedisUtil.listPushTail(redisKey, appName, key, values);
	}

	@Override
	public Long listPushHead(String redisKey, String appName, String key, String... values) {
		return RedisUtil.listPushHead(redisKey, appName, key, values);
	}

	@Override
	public String listPop(String redisKey, String appName, String key) {
		return RedisUtil.listPop(redisKey, appName, key);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T extends Serializable> Long listPushTailImplSeri(String redisKey, String appName, String key, T... values) {
		return RedisUtil.listPushTailImplSeri(redisKey, appName, key, values);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T extends Serializable> Long listPushHeadImplSeri(String redisKey, String appName, String key, T... values) {
		return RedisUtil.listPushHeadImplSeri(redisKey, appName, key, values);
	}

	@Override
	public <T extends Serializable> T listPopImplSeri(String redisKey, String appName, String key, Class<T> clazz) {
		return RedisUtil.listPopImplSeri(redisKey, appName, key, clazz);
	}

}
